home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Languages / Mops 2.7 / Mops source / Module source / casemod.txt < prev    next >
Encoding:
Text File  |  1995-11-22  |  7.6 KB  |  346 lines  |  [TEXT/MSET]

  1. \ This module handles the implementation of our case constructs
  2. \ CASE[ and SELECT[.
  3. \ Notice that we don't use any assembler at all, and only need one
  4. \ special handler word CaseJMP to compile an indexed dispatch for SELECT[.
  5. \ We compile sequences that the optimizer will pick up so that the resulting
  6. \ code is pretty well optimum anyway.  The key to this is the use of the
  7. \ pseudo-value "Treg" which is actually the machine register D1.
  8.  
  9.  
  10. type{  keyed_case  indexed_case  }
  11.  
  12. 240    constant    KEYED_CHK
  13. 250    constant    INDEXED_CHK
  14.  
  15.  
  16. : CASE[        \ ( -- Schain Fchain endChain diff end-stub? chk )
  17.             \  Implements CASE[ in main dic.
  18.     case_type                \ save over nested cases
  19.     keyed_case -> case_type
  20.     eval" -> treg"
  21.     0                    \ initial success chain
  22.     0                    \ initial fail chain
  23.     0                    \ initial end chain
  24.     0                    \ initial diff
  25.     false                \ no end of stub yet
  26.     keyed_chk            \ check value
  27.     postpone [  ;        immediate
  28.  
  29.  
  30. : ADD_ENTRY { ccmp mark-addr chk link -- link' }
  31.     link IF
  32.         link  mark-addr wdispl!        \ store new mark at prev addr if any
  33.     THEN
  34.     mark-addr                          \ mark addr is new link
  35.     ccmp $ 100 and IF 1+ THEN  ;    \ Store "dontShorten" flag in low bit
  36.  
  37.  
  38. : RESOLVE  { link \ link' nxt -- }
  39.     BEGIN
  40.         link  -2 and -> link'
  41.         link'
  42.     WHILE
  43.         link' wdisplace -> nxt
  44.         link 1 and IF  $ 100  ELSE  0  THEN
  45.         link' 120  >resolve
  46.         nxt -> link
  47.     REPEAT  ;
  48.  
  49.  
  50. : RESOLVEF  { link \ link' nxt -- }
  51.     BEGIN
  52.         link  -2 and -> link'
  53.         link'
  54.     WHILE
  55.         link' wdisplace -> nxt
  56.         link 1 and IF  $ 100  ELSE  0  THEN
  57.         link' 120  >resolve
  58.         nxt -> link
  59.         link IF  postpone literal  eval" ++> treg"  THEN
  60.     REPEAT  ;
  61.  
  62.  
  63. \ FIX_STUB is called at the end of a stub.  We do many strange and
  64. \ intricate things...
  65.  
  66. : FIX_STUB  { Schain Fchain endChain \ svMC -- endChain' }
  67.     moveCode? -> svMC
  68.     Fchain wdisplace
  69.     IF
  70.         compBR  >mark  endChain add_entry  -> endChain
  71.         false -> moveCode?
  72.         Fchain resolveF
  73.     ELSE
  74.         compBR 0 w,
  75.         Schain        \ This is Schain which we've already resolved at the
  76.                     \ the start of the stub, but we need it here to find out
  77.         IF            \ if there were several "success" items - if so, we
  78.                     \ can't shorten the "fail" branch(es)
  79.             false -> moveCode?
  80.         THEN
  81.         Fchain resolveF
  82.         2 --> DP >mark  endChain add_entry  -> endChain
  83.     THEN
  84.     svMC -> moveCode?
  85.     endChain  ;
  86.  
  87.  
  88. : NEW_STUB  {    Schain Fchain endChain
  89.                 diff end-stub? chk
  90.                 lo hi  flg
  91.                 \ svMC
  92.                 -- Schain' Fchain' endChain' diff end-stub? chk }
  93.  
  94.     postpone ]                    \ Must be compiling for evaluates below
  95.     keyed_chk chk ?pairs
  96.     end-stub?                    \ NZ if ending a stub
  97.     IF  Schain Fchain endChain fix_stub  -> endChain
  98.         0 -> Schain  0 -> Fchain
  99.     THEN
  100.  
  101.     hi lo <>
  102.     IF
  103.         lo diff -  postpone literal
  104.         eval" --> treg treg 0>= if"
  105.         Fchain add_entry -> Fchain
  106.         hi lo -
  107.         hi lo -  postpone literal
  108.         eval" --> treg treg 0<="
  109.         flg
  110.         IF  postpone if
  111.             Fchain add_entry -> Fchain
  112.         ELSE
  113.             postpone nif  dontShorten
  114.             Schain add_entry -> Schain
  115.         THEN
  116.     ELSE
  117.         hi diff -  postpone literal
  118.         eval" --> treg treg"
  119.         flg
  120.         IF    postpone nif
  121.             Fchain add_entry -> Fchain
  122.         ELSE
  123.             postpone if
  124.             Schain add_entry -> Schain
  125.         THEN
  126.     THEN
  127.     lo -> diff
  128.     flg IF
  129.         Schain resolve
  130.         Schain IF  here 2-  -> Fchain  THEN
  131.                                     \ we clear Schain next time around,
  132.                                     \  since at FIX_STUB we need to know
  133.                                     \  whether anything was on it
  134.     ELSE
  135.         hi lo <>
  136.         IF
  137.             moveCode? -> svMC
  138.             false -> moveCode?                \ For several reasons!
  139.             postpone literal  eval" ++> treg"
  140.             Fchain resolve  0 -> Fchain
  141.             svMC -> moveCode?
  142.         THEN
  143.     THEN
  144.     Schain Fchain endChain diff
  145.     flg                                \ flg is end_stub? for next time
  146.     keyed_chk  ;
  147.  
  148.  
  149. : DEFAULT  {    Schain Fchain endChain
  150.                 diff end-stub? chk
  151.                 -- endChain chk }
  152.                 
  153.     keyed_chk chk ?pairs
  154.     end-stub?
  155.     IF    Schain Fchain endChain  fix_stub  -> endChain  THEN
  156.     postpone treg
  157.     diff postpone literal  postpone +
  158.     endChain  keyed_chk 1+  ;
  159.     
  160.  
  161. : ]CASE  { endChain chk \ svMC -- }
  162.     keyed_chk 1+ chk ?pairs
  163.     moveCode? -> svMC
  164.     false -> moveCode?
  165.     endChain resolve
  166.     svMC -> moveCode?
  167.     -> case_type  ;        immediate
  168.  
  169.  
  170.  
  171. \ Now for an indexed case, with similar style syntax:
  172.  
  173.    0    value        MAXINDEX
  174.    0    value        MININDEX
  175.    
  176.    0    value        ADDRX        \ just for testing
  177.  
  178.  
  179. : SELECT[        \ ( -- lots )
  180.  
  181.     case_type  maxindex  minindex    \ Save on stack for nested cases
  182.     indexed_case  -> case_type
  183.     0 -> maxindex
  184.     big# -> minindex
  185.     compBr >mark            \ Forward branch to dispatch code
  186.     dontShorten                \ Penalty: a terrible death
  187.     1                        \ Dummy, so ]SELECT knows when to stop
  188.     0                        \ initial end chain
  189.     false                    \ no end of stub yet
  190.     indexed_chk                \ check value
  191.     postpone [  ;        immediate
  192.  
  193.  
  194.     
  195. : TBL_NEW_STUB  {  endChain end-stub? chk index flg
  196.                 -- index here endChain' end-stub? chk }
  197.  
  198.     postpone ]
  199.     index 0<  ?error 102
  200.     index maxindex max  -> maxindex
  201.     index minindex min  -> minindex
  202.     maxindex 500 > if  msg# 85  then
  203.     end-stub?
  204.     IF
  205.         compbr >mark  endChain add_entry  -> endChain
  206.     THEN
  207.     index here
  208.     endChain
  209.     flg                                \ flg is end_stub? for next time
  210.     indexed_chk  ;
  211.  
  212.  
  213. : TBL_DEFAULT  { endChain end-stub? chk
  214.                 -- dflt-addr endChain chk }
  215.  
  216.     postpone ]
  217.     end-stub? IF  compbr >mark  endChain add_entry  -> endChain  THEN
  218.     here                    \ here's where the default code will start
  219.     
  220. \ now we generate the code to recover the original index
  221.  
  222.     eval" treg 2/ -> treg"
  223.     minindex postpone literal  eval" ++> treg treg"
  224.     endChain
  225.     indexed_chk 1+  ;
  226.  
  227.  
  228. : ]SELECT ( ... index addr index addr )
  229.         { dflt-addr endChain chk \ tbl_start svDP -- }
  230.  
  231.     indexed_chk 1+ chk ?pairs
  232.     compbr >mark  endChain add_entry  -> endChain    \ wind up default stub
  233.     
  234.  \ Now we build the table:
  235.  
  236.      here -> tbl_start
  237.     maxindex minindex - 1+ 2* allot
  238.     dflt-addr tbl_start -  ( now relative to tbl_addr )
  239.     here 2-                    \ last entry addr
  240.     tbl_start
  241.     DO  ( fill table with dflt addr initially )
  242.         dup  i w!
  243.     2 +LOOP
  244.     drop
  245.     BEGIN    ( index addr )    dup 1 =
  246.     NWHILE
  247.         ( index addr )  tbl_start -  swap minindex - 2* tbl_start + w!
  248.     REPEAT
  249.     drop
  250.     
  251. \ Now we generate the dispatch code:
  252.  
  253.     ( >mark-from-initial-branch )
  254.     >resolve
  255.     eval" -> treg treg +> treg"
  256.     minindex 2* postpone literal eval" --> treg"
  257.                                     \ Compiles nothing if minindex is zero
  258.     maxindex minindex - 2* postpone literal
  259.     eval" treg u< nif"
  260.     false -> moveCode?
  261.     dflt-addr -> DP >resolve        \ branch is actually back, but that's OK
  262.                                     \  so long as we inhibit code movement
  263.     frNxtDP -> DP
  264.     tbl_start lit-addr  eval" treg + w@x -> tareg"
  265.     tbl_start lit-addr  eval" tareg +"   caseJMP
  266.     endChain resolve                \ can't move code or initial branch
  267.     true -> moveCode?                \  would be wrong
  268.  
  269.     -> minindex  -> maxindex  -> case_type  ;            immediate
  270.  
  271.  
  272. \ These words are the same in both constructs, so we work out which action
  273. \ to apply by looking at case_type.
  274.  
  275. : ]=>        case_type keyed_case =
  276.             IF        dup  true new_stub
  277.             ELSE    true tbl_new_stub
  278.             THEN  ;                        immediate
  279.             
  280. : ],        case_type keyed_case =
  281.             IF        dup false new_stub
  282.             ELSE    false tbl_new_stub
  283.             THEN  ;                        immediate
  284.  
  285. : RANGE]=>    true new_stub  ;            immediate
  286. : RANGE],    false new_stub  ;            immediate
  287.  
  288.  
  289. : DEFAULT=>    case_type keyed_case =
  290.             IF        default
  291.             ELSE    tbl_default
  292.             THEN  ;                        immediate
  293.  
  294.  
  295. endload
  296.  
  297. +echo
  298.  
  299. \ Something as complicated as that needs a bit of systematic testing...
  300.  
  301. : qq db
  302.     case[ 21 ]=> 210
  303.         [ 22 ]=> 220
  304.         [ 80 ], [ 82 ], [ 84 ], [ 86 ]=> 888
  305.         [ 30 40 range]=> 333
  306.         [ 90 ], [ 92 ], [ 170 ]=> -999
  307.         [ 90 ], [ 92 ], [ 100 150 range], [ 170 ]=> -999
  308.         [ 222 ]=>  2220
  309.       default=> 99
  310.      ]case  ;
  311.  
  312. : q db
  313.     select[    3 ]=> 23
  314.           [ 2 ]=> 22
  315. \          [ 0 ]=> 20
  316.           [ 8 ]=> 28
  317.     default=> 999
  318.     ]select  ;
  319.  
  320. : ?CHK    <> abort" check FAILED!!!"  ;
  321.  
  322. \ endload
  323.  
  324. +echo
  325. 21 qq  210 ?chk
  326. 22 qq  220 ?chk
  327. 80 qq  888 ?chk
  328. 84 qq  888 ?chk
  329. 85 qq  99  ?chk  85 ?chk
  330. 35 qq  333 ?chk
  331. 92 qq  -999 ?chk
  332. 120 qq -999 ?chk
  333. 170 qq -999 ?chk
  334. 222 qq 2220 ?chk
  335. 9999 qq 99 ?chk 9999 ?chk
  336.  
  337. 3 q        23    ?chk
  338. 2 q        22    ?chk
  339. 8 q        28    ?chk
  340. 6 q        999    ?chk  6 ?chk
  341. -1 q    999    ?chk  -1 ?chk
  342. 9  q    999    ?chk  9 ?chk
  343.  
  344.  
  345. \ torture tests WORKED!
  346.